import geom3
import colour
import sphere
import plane
import visual

ARROW_SHAFT_SIZE = 0.01
INF_ARROW_LENGTH = 5
MAX_ARROW_LENGTH = 5

PLANE_LENGTH = 1
PLANE_WIDTH = 1
PLANE_HEIGHT= 0.001

class Retarded:
    """Class that does visual debugging for a ray tracer application."""

    def __init__(self, num_rows, num_columns, scene, rayCastFunction, showImageFunc):
        """Create the ray-tracing debugger."""
        self.num_rows = num_rows
        self.num_columns = num_columns
        self.rawScene = scene
        self.visualScene = toVisualScene(scene)
        self.rayCastFunction = rayCastFunction
        self.row = 0
        self.col = 0
        self.rays = [[[] for row in range(num_rows)] for col in range(num_columns)]
        self.showImageFunc = showImageFunc

    def castAllRays(self):
        """Cast every ray there is."""
        self.gotoFirstRay()
        self.showCurrentRay()
        for theRow in range (0, self.num_rows):
            for theCol in range (0, self.num_columns):
                self.showNextRay(True)

    def showScene(self):
        """Show the scene of geometry objects as a 'visual' scene."""
        for obj in self.visualScene:
            obj.visible = 1

    def hideScene(self):
        """Hide the scene of geometry objects as a 'visual' scene."""
        for obj in self.visualScene:
            obj.visible = 0

    def gotoNextRay(self):
        """Go to the next ray."""
        self.col += 1
        if self.col >= self.num_columns:
            self.gotoNextRow()
    
    def showNextRay(self, hideCurrent=False):
        """Show the next ray to be drawn by the caster."""
        if hideCurrent:
            self.hideCurrentRay()
        self.gotoNextRay()
        self.showCurrentRay()

    def gotoPrevRay(self):
        """Go to the next ray."""
        self.col -= 1
        if self.col < 0:
            self.gotoPrevRow()

    def showPrevRay(self, hideCurrent=False):
        """Show the previous ray to be drawn by the caster."""
        if hideCurrent:
            self.hideCurrentRay()
        self.gotoPrevRay()
        self.showCurrentRay()

    def showCurrentRay(self):
        """Make the current ray visible."""
        if self.currentRay() is not None:
            for ray in self.currentRay():
                ray.visible = 1

    def hideCurrentRay(self):
        """Hides the ray that is currently drawn."""
        if self.currentRay() is not None:
            for ray in self.currentRay():
                ray.visible = 0

    def gotoNextRow(self):
        """Go to the start of the next row."""
        if self.row < self.num_rows - 1:
            self.row += 1
            self.col = 0
        else:
            self.gotoFirstRay()

    def showNextRow(self, hideCurrent=False):
        """Show the next row."""
        if hideCurrent:
            self.hideCurrentRow()
        self.gotoNextRow()
        self.showCurrentRow()

    def gotoPrevRow(self):
        """Go to the start of the previous row."""
        if self.row > 0:
            self.row -= 1
            self.col = 0
        else:
            self.gotoLastRay()

    def showPrevRow(self, hideCurrent=False):
        """Show the next row."""
        if hideCurrent:
            self.hideCurrentRow()
        self.gotoPrevRow()
        self.showCurrentRow()

    def showCurrentRow(self):
        """Show the whole current row."""
        for theCol in range (0, self.num_columns):
            self.col = theCol
            self.showCurrentRay()

    def hideCurrentRow(self):
        """Hide the whole current row."""
        for theCol in range (0, self.num_columns):
            self.col = theCol
            self.hideCurrentRay()

    def gotoFirstRay(self):
        """Goes to the first ray to be traced."""
        self.row = 0
        self.col = 0

    def gotoLastRay(self):
        """Goes to the last ray to be traced."""
        self.row = self.num_rows - 1
        self.col = self.num_columns - 1

    def currentRay(self):
        """Return the ray at the current coordinates. Cast it if need be."""
        return self.rayAt(self.row, self.col)

    def hideRay(self, row, col):
        """Hide the ray at the given coordinates."""
        for singleRay in self.rayAt(row, col):
            singleRay.visible = 0

    def showRay(self, row, col):
        """Show the ray at the given coordinates."""
        for singleRay in self.rayAt(row, col):
            singleRay.visible = 1

    def rayAt(self, row, col):
        """Return the ray at the given coordinates. Cast it if need be."""
        if self.rays[row][col] == []:
             self.rayCastFunction(row, col)
        return self.rays[row][col]

    def hideAllRays(self):
        """Hide all the rays."""
        for theRow in range (0, self.num_rows):
            for theCol in range (0, self.num_columns):
                self.hideRay(theRow, theCol)

    def addRay(self, ray, colour, t=None):
        """Add a ray to be visualized."""
        visualRay = toVisualArrow(ray, colour, t)
        
        if self.rays[self.row][self.col] is None:
            self.rays[self.row][self.col] = [visualRay]
        else:
            self.rays[self.row][self.col].append(visualRay)

    def doMainLoop(self):
        """Do the main loop of the retarded project."""
        self.showScene()
        self.castAllRays()
        
        while(True):
            if self.userInput() == False:
                break
    
    def userInput(self):
        """Get keyboard input from the user."""
            
        userChoice = raw_input('Choose option:')

        if userChoice == 'q':
            return False
        elif userChoice == 'n':
            self.showNextRow(True)
        elif userChoice == '':
            self.showNextRay(True)
        elif userChoice == 'p':
            self.showPrevRow(True)
        elif userChoice == 'row':
            try:
                row = int(raw_input("Enter the row:"))
            except ValueError:
                print 'Invalid Number'
            self.hideAllRays()
            self.row = row
            self.col = 0
            self.showCurrentRay()
        elif userChoice == 'c':
            self.hideAllRays()
        elif userChoice == 'image':
            self.showImageFunc()
        elif userChoice == 'show scene':
            self.showScene()
        elif userChoice == 'hide scene':
            self.hideScene()
            
        return True

def toVisualArrow(ray, colour, t=None):
    """Convert a Ray3 to a visual arrow, and return the arrow."""
    startVector = (ray.start.x, ray.start.y, ray.start.z)
    theAxis = visual.vector(ray.dir.dx, ray.dir.dy, ray.dir.dz)
    theColour = (colour.r, colour.g, colour.b)
    
    if t is None:
        theAxis *= INF_ARROW_LENGTH
    else:
        theAxis *= min(t, MAX_ARROW_LENGTH)
        
    theShaftWidth = ARROW_SHAFT_SIZE
    theArrow = visual.arrow(pos=startVector, axis=theAxis, shaftwidth=theShaftWidth, color=theColour, fixedwidth=1)

    return theArrow

def toVisualScene(scene):
    """Convert a gemo3 scene to a 'visual' scene."""
    visualScene = []
    
    for obj in scene.objs:
        if isinstance(obj, sphere.Sphere):
            visualScene.append(toVisualSphere(obj))
        elif isinstance(obj, plane.Plane):
            visualScene.append(toVisualPlane(obj))
            
    return visualScene

def toVisualSphere(richardSphere):
    """Converts a 'Richard Lobb' sphere into a 'visual' sphere and returns it."""
    return visual.sphere(pos=toVisualPoint(richardSphere.centre),
                         color=toVisualColour(richardSphere.material.diffuseColour),
                         radius=richardSphere.radius)

def toVisualPlane(richardPlane):
    """Converts a 'Richard Lobb' plane into a 'visual' plane and returns it."""
    return visual.box(pos=toVisualPoint(richardPlane.point),
                      up=toTuple(richardPlane.normal),
                      length=PLANE_LENGTH,
                      height=PLANE_HEIGHT,
                      width=PLANE_WIDTH,
                      colour=toVisualColour(richardPlane.material.diffuseColour))

def toVisualPoint(richardPoint):
    """Converts a 'Richard Lobb' point into a 'visual' point and returns it."""
    return (richardPoint.x, richardPoint.y, richardPoint.z)

def toTuple(richardVector):
    """Converts a 'Richard Lobb' vector into a tuple and returns it."""
    return (richardVector.dx, richardVector.dy, richardVector.dz)

def toVisualColour(richardColour):
    """Converts a 'Richard Lobb' colour into a 'visual' colour and returns it."""
    return (richardColour.r, richardColour.g, richardColour.b)

if __name__ == "__main__":
    print "No demo yet."


        
